<html xmlns="http://www.w3.org/1999/xhtml">
<--源码仅供学习，请勿用于商业用途-->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>atom monitor,extremely fun!</title>
<style>
body {
    margin: 0;
}
canvas {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    cursor: crosshair;
}
#instructions {
    position: fixed;
    top: 0;
    left: 0;
    color: white;
    font-weight: 900;
    padding: 15px;
    font-family: 'Open Sans', sans-serif;
    background-color: #2fa1d6;
    transition: all .5s;
    transform: translate(0, 0);
}
#instructions.hide {
    transform: translate(0, -100%);
}
#instructions > i {
    margin-left: 9px;
    cursor: pointer;
}
</style>
</head>
 
<body>
<script src="script/main_jp.js"></script>
<canvas id="trails"></canvas>
<canvas id="particles"></canvas>
<div id="instructions">点击任意位置移动原子核<i class="fa fa-close"></i>
</div>
<script>
"use strict";
 
function _possibleConstructorReturn(self, call) {
    if (!self) {
        throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
    }
    return call && (typeof call === "object" || typeof call === "function") ? call : self;
}
 
function _inherits(subClass, superClass) {
    if (typeof superClass !== "function" && superClass !== null) {
        throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
    }
    subClass.prototype = Object.create(superClass && superClass.prototype, {
        constructor: {
            value: subClass,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}
 
function _classCallCheck(instance, Constructor) {
    if (!(instance instanceof Constructor)) {
        throw new TypeError("Cannot call a class as a function");
    }
}
 
//vector class
 
var Vector = function() {
    function Vector() {
        var x = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];
        var y = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1];
 
        _classCallCheck(this, Vector);
 
        this.x = x;
        this.y = y;
        this.magnitude = Math.sqrt(x * x + y * y);
        this.angle = Math.atan2(y, x);
    }
 
    Vector.prototype.add = function add(v) {
        this.x = this.x + v.x;
        this.y = this.y + v.y;
        this.magnitude = Math.sqrt(this.x * this.x + this.y * this.y);
        this.angle = Math.atan2(this.y, this.x);
        return this;
    };
 
    Vector.prototype.subtract = function subtract(v) {
        this.x = this.x - v.x;
        this.y = this.y - v.y;
        this.magnitude = Math.sqrt(this.x * this.x + this.y * this.y);
        this.angle = Math.atan2(this.y, this.x);
        return this;
    };
 
    Vector.prototype.setAngle = function setAngle(angle) {
        this.angle = angle;
        this.x = this.magnitude * Math.cos(angle);
        this.y = this.magnitude * Math.sin(angle);
        return this;
    };
 
    Vector.prototype.setMagnitude = function setMagnitude(magnitude) {
        this.magnitude = magnitude;
        this.x = Math.cos(this.angle) * magnitude;
        this.y = Math.sin(this.angle) * magnitude;
        return this;
    };
 
    return Vector;
}();
 
//particle class
 
var Particle = function() {
    function Particle(opts) {
        _classCallCheck(this, Particle);
 
        this.x = opts.x || Math.random() * cW;
        this.y = opts.y || Math.random() * cH;
        this.radius = opts.radius || 15;
        this.v = opts.v || new Vector();
        this.acc = opts.acc || new Vector();
        this.mass = opts.mass || 40;
        this.color = opts.color || 320;
        this.maxV = opts.maxV || 8;
        this.maxA = opts.maxA || 0.5;
        this.tasteTheRainbow = opts.tasteTheRainbow || false;
        if (opts.trail) {
            this.trail = true;
            this.trailLength = opts.trailLength || 10;
            this.trajPoints = new Queue([]);
        }
    }
 
    Particle.prototype.accelerate = function accelerate() {
        this.acc.magnitude = this.acc.magnitude > this.maxA ? this.acc.setMagnitude(this.maxA) : this.acc.magnitude;
        this.v.add(this.acc);
    };
 
    Particle.prototype.isOnScreen = function isOnScreen() {
        return this.x <= cW || this.x >= 0 || this.y <= cH || this.y >= 0;
    };
 
    Particle.prototype.update = function update() {
        if (this.acc.magnitude) {
            this.accelerate();
        }
        if (this.trail) {
            var point = {
                x: this.x,
                y: this.y
            };
            this.trajPoints.enqueue(point);
            if (this.trajPoints.getLength() >= this.trailLength) {
                this.trajPoints.dequeue();
            }
        }
        this.v.magnitude = this.v.magnitude > this.maxV ? this.v.setMagnitude(this.maxV) : this.v.magnitude;
        this.x += this.v.x;
        this.y += this.v.y;
        if (this.tasteTheRainbow) {
            this.color = this.color <= 360 ? ++this.color : 1;
        }
    };
 
    Particle.prototype.render = function render(context) {
        var trailContext = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1];
 
        context.beginPath();
        context.fillStyle = "hsl(" + this.color + ", 100%, 50%)";
        context.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
        context.fill();
        context.closePath();
        if (this.trail && trailContext) {
            var trajectory = this.trajPoints;
            trailContext.beginPath();
            trailContext.strokeStyle = "hsl(" + this.color + ", 100%, 50%)";
            trailContext.lineWidth = 0.2;
            trailContext.moveTo(trajectory.queue[0].x, trajectory.queue[0].y);
            for (var i = 1, len = trajectory.getLength(); i < len; i++) {
                trailContext.lineTo(trajectory.queue[i].x, trajectory.queue[i].y);
            }
            trailContext.stroke();
            trailContext.closePath();
        }
    };
 
    return Particle;
}();
 
var Planet = function(_Particle) {
    _inherits(Planet, _Particle);
 
    function Planet() {
        _classCallCheck(this, Planet);
 
        return _possibleConstructorReturn(this, _Particle.apply(this, arguments));
    }
 
    Planet.prototype.gravitate = function gravitate(p) {
        if (Particle.prototype.isPrototypeOf(p)) {
            var d = Math.sqrt((this.x - p.x) * (this.x - p.x) + (this.y - p.y) * (this.y - p.y));
            var attractiveForce = p.mass * this.mass / (d * d);
            this.acc.setAngle(Math.atan2(p.y - this.y, p.x - this.x)).setMagnitude(attractiveForce);
        } else {
            throw new Error("The argument passed to the gravitate function must be a particle");
        }
        this.update();
    };
 
    Planet.prototype.gravitateStarCluster = function gravitateStarCluster(cluster) {
        var gV = new Vector();
        for (var i = 0; i < cluster.length; i++) {
            var _star = cluster[i];
            if (Particle.prototype.isPrototypeOf(_star)) {
                var v = new Vector();
                var d = Math.sqrt((this.x - _star.x) * (this.x - _star.x) + (this.y - _star.y) * (this.y - _star.y));
                var attractiveForce = _star.mass * this.mass / (d * d);
                v.setAngle(Math.atan2(_star.y - this.y, _star.x - this.x)).setMagnitude(attractiveForce);
                gV = gV.add(v);
            } else {
                throw new Error("The argument supplied to the gravitateStarCluster function must be an array of particles");
            }
        }
        this.acc.setAngle(gV.angle).setMagnitude(gV.magnitude);
        this.update();
    };
 
    return Planet;
}(Particle);
 
var Queue = function() {
    function Queue(array) {
        _classCallCheck(this, Queue);
 
        this.queue = array;
    }
 
    Queue.prototype.getLength = function getLength() {
        return this.queue.length;
    };
 
    Queue.prototype.enqueue = function enqueue(element) {
        this.queue.unshift(element);
    };
 
    Queue.prototype.dequeue = function dequeue() {
        this.queue.pop();
    };
 
    Queue.prototype.display = function display() {
        for (var i = 0; i < this.getLength; i++) {
            console.log(this.queue[i]);
        }
    };
 
    return Queue;
}();
 
//util function to paint entire canvas of specified color
 
function paintCanvas(color, context) {
    var W = context.canvas.clientWidth;
    var H = context.canvas.clientHeight;
    context.save();
    context.fillStyle = color;
    context.fillRect(0, 0, W, H);
    context.restore();
}
 
//util function that returns a random number in a given range
function randomInRange(min, max) {
    var result = min + Math.random() * (max - min);
    return result;
}
 
//////////////////////////////////////
// -- THIS ANIMATION'S VARIABLES -- //
//////////////////////////////////////
 
//canvas
var trailCanvas = document.getElementById('trails');
var particlesCanvas = document.getElementById('particles');
var trailCtx = trailCanvas.getContext('2d');
var particleCtx = particlesCanvas.getContext('2d');
 
var cW = particlesCanvas.width = trailCanvas.width = window.innerWidth;
var cH = particlesCanvas.height = trailCanvas.height = window.innerHeight;
 
//animation constants
var settings = {
 
    STAR_MASS: 1500,
    PLANET_MASS: 20,
    PLANET_V_X: 2,
    P_TRAIL: true,
    P_MAX_VELOCITY: 8,
    P_MAX_ACC: 0.5,
    P_MIN_VELOCITY: 5,
    PARTICLE_NUM: 70,
    BOUNDS: false,
    TRAIL_LENGTH: 90,
    TRAIL_CNVS: trailCanvas,
    PARTICLE_CNVS: particlesCanvas,
    COLOR: 320,
    TRAIL_CTXT: trailCtx,
    TASTETHERAINBOW: true,
    PARTICLE_CTXT: particleCtx
 
};
 
window.addEventListener('resize', function() {
    cW = particlesCanvas.width = trailCanvas.width = window.innerWidth;
    cH = particlesCanvas.height = trailCanvas.height = window.innerHeight;
});
 
//mouse events and stuff
var mX = -1;
var mY = -1;
var draggingStar = false;
 
document.addEventListener('mousemove', function(e) {
    mX = e.clientX;
    mY = e.clientY;
});
 
settings.PARTICLE_CNVS.addEventListener('click', function() {
    draggingStar = !draggingStar;
});
 
//stars and particles
var s = [];
var p = [];
 
var star = new Particle({
    x: cW / 2,
    y: cH / 2,
    radius: 15,
    color: settings.COLOR,
    tasteTheRainbow: settings.TASTETHERAINBOW,
    mass: settings.STAR_MASS
});
 
for (var i = 0; i < settings.PARTICLE_NUM; i++) {
 
    var planet = new Planet({
        x: Math.random() * cW,
        y: Math.random() * cH,
        radius: 2,
        mass: settings.PLANET_MASS,
        trail: settings.P_TRAIL,
        trailLength: settings.TRAIL_LENGTH,
        color: settings.COLOR,
        maxV: settings.P_MAX_VELOCITY,
        maxA: settings.P_MAX_ACC,
        tasteTheRainbow: settings.TASTETHERAINBOW,
        v: new Vector(Math.random() < 0.5 ? -settings.P_MIN_VELOCITY : settings.P_MIN_VELOCITY, 0)
    });
 
    p.push(planet);
}
 
//animation function
function animate() {
 
    settings.PARTICLE_CTXT.clearRect(0, 0, cW, cH);
 
    settings.TRAIL_CTXT.clearRect(0, 0, cW, cH);
    paintCanvas('black', settings.TRAIL_CTXT);
 
    star.update();
    star.render(settings.PARTICLE_CTXT);
 
    for (var i = 0; i < p.length; i++) {
        p[i].gravitate(star);
        if (settings.BOUNDS) {
            if (p[i].x > cW) {
                p[i].x = cW;
            }
            if (p[i].x < 0) {
                p[i].x = 0;
            }
            if (p[i].y > cH) {
                p[i].y = cH;
            }
            if (p[i].y < 0) {
                p[i].y = 0;
            }
        }
        if (p[i].isOnScreen()) {
            p[i].render(settings.PARTICLE_CTXT, settings.TRAIL_CTXT);
        }
    }
 
    if (draggingStar) {
        star.x += (mX - star.x) * 0.1;
        star.y += (mY - star.y) * 0.1;
    }
 
    requestAnimationFrame(animate);
}
 
//start loop!
animate();
 
//datgui thangs
var gui = new dat.GUI();
gui.add(settings, 'STAR_MASS', 500, 10000).name('star mass').onFinishChange(function() {
    star.mass = settings.STAR_MASS;
});
gui.add(settings, 'P_TRAIL').name('particle trail').onFinishChange(function() {
    p = [];
    settings.TRAIL_CTXT.clearRect(0, 0, cW, cH);
    for (var i = 0; i < settings.PARTICLE_NUM; i++) {
        var planet = new Planet({
            x: Math.random() * cW,
            y: Math.random() * cH,
            radius: 2,
            mass: settings.PLANET_MASS,
            trail: settings.P_TRAIL,
            trailLength: settings.TRAIL_LENGTH,
            color: settings.COLOR,
            maxV: settings.P_MAX_VELOCITY,
            maxA: settings.P_MAX_ACC,
            tasteTheRainbow: settings.TASTETHERAINBOW,
            v: new Vector(Math.random() < 0.5 ? -settings.P_MIN_VELOCITY : settings.P_MIN_VELOCITY, 0)
        });
        p.push(planet);
    }
    star.color = settings.COLOR;
});
gui.add(settings, 'P_MAX_VELOCITY', 4, 14).name('max velocity').onFinishChange(function() {
    p = [];
    settings.TRAIL_CTXT.clearRect(0, 0, cW, cH);
    for (var i = 0; i < settings.PARTICLE_NUM; i++) {
        var planet = new Planet({
            x: Math.random() * cW,
            y: Math.random() * cH,
            radius: 2,
            mass: settings.PLANET_MASS,
            trail: settings.P_TRAIL,
            trailLength: settings.TRAIL_LENGTH,
            color: settings.COLOR,
            maxV: settings.P_MAX_VELOCITY,
            maxA: settings.P_MAX_ACC,
            tasteTheRainbow: settings.TASTETHERAINBOW,
            v: new Vector(Math.random() < 0.5 ? -settings.P_MIN_VELOCITY : settings.P_MIN_VELOCITY, 0)
        });
        p.push(planet);
    }
    star.color = settings.COLOR;
});
gui.add(settings, 'P_MAX_ACC', 0.2, 2).name('max acceleration').onFinishChange(function() {
    p = [];
    settings.TRAIL_CTXT.clearRect(0, 0, cW, cH);
    for (var i = 0; i < settings.PARTICLE_NUM; i++) {
        var planet = new Planet({
            x: Math.random() * cW,
            y: Math.random() * cH,
            radius: 2,
            mass: settings.PLANET_MASS,
            trail: settings.P_TRAIL,
            trailLength: settings.TRAIL_LENGTH,
            color: settings.COLOR,
            maxV: settings.P_MAX_VELOCITY,
            maxA: settings.P_MAX_ACC,
            tasteTheRainbow: settings.TASTETHERAINBOW,
            v: new Vector(Math.random() < 0.5 ? -settings.P_MIN_VELOCITY : settings.P_MIN_VELOCITY, 0)
        });
        p.push(planet);
    }
    star.color = settings.COLOR;
});
gui.add(settings, 'PARTICLE_NUM', 1, 250).name('particles number').onFinishChange(function() {
    p = [];
    settings.TRAIL_CTXT.clearRect(0, 0, cW, cH);
    for (var i = 0; i < settings.PARTICLE_NUM; i++) {
        var planet = new Planet({
            x: Math.random() * cW,
            y: Math.random() * cH,
            radius: 2,
            mass: settings.PLANET_MASS,
            trail: settings.P_TRAIL,
            trailLength: settings.TRAIL_LENGTH,
            color: settings.COLOR,
            maxV: settings.P_MAX_VELOCITY,
            maxA: settings.P_MAX_ACC,
            tasteTheRainbow: settings.TASTETHERAINBOW,
            v: new Vector(Math.random() < 0.5 ? -settings.P_MIN_VELOCITY : settings.P_MIN_VELOCITY, 0)
        });
        p.push(planet);
    }
    star.color = settings.COLOR;
});
gui.add(settings, 'BOUNDS').name('bounds');
gui.add(settings, 'TRAIL_LENGTH', 10, 200).name('trail length').onFinishChange(function() {
    settings.TRAIL_CTXT.clearRect(0, 0, cW, cH);
    for (var i = 0; i < settings.PARTICLE_NUM; i++) {
        p[i].trajPoints = new Queue([]);
        p[i].trailLength = settings.TRAIL_LENGTH;
    }
});
 
//for debugging without printing stuff 1000000000 times in the console
//window.setInterval(function(){ console.log(); }, 2000);
 
//instructions panel script
var instructionsPanel = document.querySelector('#instructions');
var closeButton = document.querySelector('#instructions > i');
 
closeButton.addEventListener('click', function() {
    instructionsPanel.classList.add('hide');
});
</script>
</body>
</html>
